#pragma once
#include <string>
#include <unistd.h>
#include <ctime>
#include <cstdarg>
#include <fstream>
#include "LockGuard.hpp"

namespace LogModule
{
#define FLUSH_SCREEN 1
#define FLUSH_FILE 2

    const std::string g_file = "./log.txt";              // 默认的日志文件
    pthread_mutex_t g_mutex = PTHREAD_MUTEX_INITIALIZER; // 定义一把全局的互斥锁

    // 日志的等地枚举
    enum
    {
        DEBUG = 1, // 调试
        INFO,      // 正常消息
        WARNING,   // 警告
        ERROR,     // 错误
        FATAL      // 致命错误
    };

    // 描述日志的属性
    struct log_message
    {
        int _level;             // 日志登记
        pid_t _id;              // 当前进程的pid
        std::string _file_name; // 文件名称
        int _file_line;         // 在文件的第几行
        std::string _curr_time; // 当前的时间
        std::string _log_msg;   // 日志信息
    };

    class Log
    {
    private:
        std::string GetCurrTime()
        {
            time_t now = time(nullptr);
            struct tm *t = localtime(&now);
            char buffer[128];
            snprintf(buffer, sizeof(buffer), "%d-%02d-%02d %02d:%02d:%02d",
                     t->tm_year + 1900,
                     t->tm_mon + 1,
                     t->tm_mday,
                     t->tm_hour,
                     t->tm_min,
                     t->tm_sec);

            return buffer;
        }

        std::string LevelToString(int level)
        {
            switch (level)
            {
            case DEBUG:
                return "DEBUG";
            case INFO:
                return "INFO";
            case WARNING:
                return "WARNING";
            case ERROR:
                return "ERROR";
            case FATAL:
                return "FATAL";
            default:
                return "UNKNOWN";
            }
        }

    void FlushToScreen(const log_message &lg)
    {
        printf("[%s][%d][%s][%d][%s] %s",
                LevelToString(lg._level).c_str(),
                lg._id,
                lg._file_name.c_str(),
                lg._file_line,
                lg._curr_time.c_str(),
                lg._log_msg.c_str());
    }

    void FlushToFile(const log_message &lg)
    {
        std::ofstream out(_file, std::ios::app);
        if (!out.is_open())
            return;
        char buffer[1024];
        snprintf(buffer, sizeof(buffer), "[%s][%d][%s][%d][%s] %s",
                    LevelToString(lg._level).c_str(),
                    lg._id,
                    lg._file_name.c_str(),
                    lg._file_line,
                    lg._curr_time.c_str(),
                    lg._log_msg.c_str());
        out << buffer;
        out.close();
    }

    void FlushLog(const log_message &lg)
    {
        // 将来多执行流访问时，防止其他执行流修改_type所以将他保护
        LockGuard lock(&g_mutex);
        switch (_type)
        {
        case FLUSH_SCREEN:
            FlushToScreen(lg);
            break;
        case FLUSH_FILE:
            FlushToFile(lg);
            break;
        }
    }

    public:
        // 默认的日志文件是全局的log.txt 默认的刷新方式是 向屏幕刷新
        Log(const std::string file = g_file)
            : _type(FLUSH_SCREEN), _file(file)
        {
        }

        ~Log()
        {
        }

        void LogMessage(int level, const std::string &file_name, int file_line, const char *fomat, ...)
        {
            log_message lg;
            lg._level = level;
            lg._id = getpid();
            lg._file_name = file_name;
            lg._file_line = file_line;
            lg._curr_time = GetCurrTime();

            va_list ap;
            va_start(ap, fomat);
            char buffer[128];
            vsnprintf(buffer, sizeof(buffer), fomat, ap);
            va_end(ap);
            lg._log_msg = buffer;

            // 刷新日志
            FlushLog(lg);
        }

        void Enable(int type)
        {
            _type = type;
        }

    private:
        int _type;         // 日志刷新类型
        std::string _file; // 刷新日志的文件
    };

    Log log;

#define LOG(Level, Fomat, ...)                                           \
    do                                                                   \
    {                                                                    \
        log.LogMessage(Level, __FILE__, __LINE__, Fomat, ##__VA_ARGS__); \
    } while (0)

#define ENABLE_SCREEN()           \
    do                            \
    {                             \
        log.Enable(FLUSH_SCREEN); \
    } while (0)

#define ENABLE_FILE()           \
    do                          \
    {                           \
        log.Enable(FLUSH_FILE); \
    } while (0)
} // namespace LogMoudle